﻿using UnityEngine;
using System.Collections.Generic;

//[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class SplineMesh : MonoBehaviour {

	public CubicBezierSpline path;
	public Material _mate;
	public float lineWidth = 0.1f;
	[Range(1, 100)]
	public int divisionsPerCurve;
	public float uvTiling = 1f;
	private float separationBetweenDivisions { get { return 1f/(float)divisionsPerCurve/(float)	path.curveCount; } }
	public bool isRealTime = false;
	float offset = 1;

	Mesh bodyMesh, circleMesh;

	List<Vector3> vertices;
	List<int> triangles;
	Node[,] nodes;
	List<Vector2> uvs;

	List<Vector3> circleVertices;
	List<int> circleTriangles;
	List<Vector2> circleUvs;

	public GameObject controlPoint;

	void Start(){
//		GetComponent<MeshFilter> ().mesh.Clear ();
		bodyMesh = new Mesh ();
		circleMesh = new Mesh ();
		Build();
		initControl ();
	}

	void initControl() {
		for (int i = 0; i < path.ControlPointCount; i++) {
			var point = path.ControlPoints [i];
			var cpoint = Instantiate (controlPoint, new Vector3 (point.x, point.y, -500), Quaternion.identity, this.transform).GetComponent<ControlPoint> ();
			cpoint.index = i;
		}
	}

	public void setControlPointVector(Vector3 point, int index) {
		path.ControlPoints [index] = new Vector3 (point.x, point.y, 0);
	}

	[ContextMenu("Build Mesh")]
	void Build(){
		GenerateMesh();
		MakeMeshs ();
		DisplayMesh();
	}

	public void GenerateMesh(){
		vertices = new List<Vector3>();
		triangles = new List<int>();
		uvs = new List<Vector2>();

		circleVertices = new List<Vector3> ();
		circleUvs = new List<Vector2> ();
		circleTriangles = new List<int> ();

		offset = 1 / uvTiling;
		int nodeRowCount = (path.curveCount * divisionsPerCurve) + 1;
		nodes = new Node[nodeRowCount, 3];

		//This creates all the points necesary along the curve.
		for(int i = 0; i < nodeRowCount; i++){
			float newi = separationBetweenDivisions * i;
			Vector3 controlPoint = path.GetPointInCurve(newi);
			for(int j = 0; j < 2; j++){
				Vector3 perpControlPoint = path.PerpendicularOfPoint(newi).normalized * (j == 0 ? 1 : -1);
				//Change Width should be around here
				nodes[i,j] = new Node(controlPoint + perpControlPoint * lineWidth);
			}
			nodes [i, 2] = new Node (new Vector3 (controlPoint.x, controlPoint.y, - 256f));
		}

		MakeCircle ((int)lineWidth * 3);

		//Creates points 
		Square[] squares = new Square[nodeRowCount-1];
		for(int i = 0; i < squares.Length; i++){
			squares[i] = new Square(nodes[i,	1],		//Top Left
				nodes[i+1,	1],		//Top Right
				nodes[i+1,	0],		//Bottom Right
				nodes[i,	0], 	//Botom Left
				nodes[i,    2],  	//Center Left
				nodes[i+1,	2]		//Center Right
			);
		}
			
		for (int i = 0; i < squares.Length; i++){
			AddVertices(squares[i]);
			AddUVs(squares[i], i);
			AddTriangles(i);
		}
	}

	void MakeCircle(int numOfPoints)
	{
		float radius = lineWidth;
		circleVertices.Add (new Vector3 (0, 0, -256f)); // center
		for (int i = 0; i < numOfPoints - 1; i++) {
			float angle = (i - 1) * Mathf.PI / radius;
			circleVertices.Add (new Vector3 (
				radius * Mathf.Sin(angle),
				- radius * Mathf.Cos(angle)
			));
		}

		circleUvs.Add (Vector2.one);
		for (int i = 0; i < numOfPoints - 1; i++) {
			circleUvs.Add (Vector2.zero);
		}

		for (int i = 1; i < numOfPoints - 1; i++) {
			circleTriangles.Add(0);
			circleTriangles.Add( i );
			circleTriangles.Add( i + 1 );
		}
	}

	void AddVertices(Square square){
		vertices.Add (square.bottomLeft.position);
		vertices.Add (square.bottomRight.position);
		vertices.Add (square.centerLeft.position);
		vertices.Add (square.centerRight.position);
		vertices.Add (square.topLeft.position);
		vertices.Add (square.topRight.position);
	}

	void AddUVs(Square square, int index){
		uvs.Add (Vector2.zero);
		uvs.Add (Vector2.zero);
		uvs.Add (Vector2.one);
		uvs.Add (Vector2.one);
		uvs.Add (Vector2.zero);
		uvs.Add (Vector2.zero);
	}

	void AddTriangles(int index){
		/*
			vertices.Add (square.bottomLeft.position); 0
			vertices.Add (square.bottomRight.position); 1
			vertices.Add (square.centerLeft.position); 2
			vertices.Add (square.centerRight.position); 3
			vertices.Add (square.topLeft.position); 4
			vertices.Add (square.topRight.position); 5
		 */
		triangles.Add( (index * 6) + 4 );
		triangles.Add( (index * 6) + 5 );
		triangles.Add( (index * 6) + 3 );

		triangles.Add( (index * 6) + 3 );
		triangles.Add( (index * 6) + 2 );
		triangles.Add( (index * 6) + 4 );

		triangles.Add( (index * 6) + 2 );
		triangles.Add( (index * 6) + 3 );
		triangles.Add( (index * 6) + 1 );

		triangles.Add( (index * 6) + 1 );
		triangles.Add( (index * 6) + 0 );
		triangles.Add( (index * 6) + 2 );
	}

	public void DisplayMesh(){
		for (int i = 0; i < nodes.Length / 3; i++) {
			var pos = nodes [i, 2].position;
			Graphics.DrawMesh (circleMesh, new Vector3(pos.x, pos.y) + gameObject.transform.position, Quaternion.identity, _mate, 0);
		}

		var pos1 = nodes[nodes.Length / 3 - 1, 2].position;
		Graphics.DrawMesh (circleMesh, new Vector3(pos1.x, pos1.y) + gameObject.transform.position, Quaternion.identity, _mate, 0);

		Graphics.DrawMesh (bodyMesh, gameObject.transform.position, Quaternion.identity, _mate, 0);
	}

	public void MakeMeshs(){
		circleMesh.vertices = circleVertices.ToArray ();
		circleMesh.uv = circleUvs.ToArray ();
		circleMesh.triangles = circleTriangles.ToArray ();
		circleMesh.RecalculateNormals ();

		bodyMesh.vertices = vertices.ToArray();
		bodyMesh.uv = uvs.ToArray();
		bodyMesh.triangles = triangles.ToArray();
		bodyMesh.RecalculateNormals();
	}

	void Update(){
		if(!isRealTime)
			return;
		Build();
	}

	public class Node {
		public Vector3 position;
		public int vertexIndex = -1;
		
		public Node(Vector3 _pos) {
			position = _pos;
		}
	}

	public class Square {
		
		public Node topLeft, topRight, bottomRight, bottomLeft, centerLeft, centerRight;
		
		public Square (Node _topLeft, Node _topRight, Node _bottomRight, Node _bottomLeft, Node _centerLeft, Node _centerRight) {
			topLeft = _topLeft;
			topRight = _topRight;
			bottomRight = _bottomRight;
			bottomLeft = _bottomLeft;
			centerLeft = _centerLeft;
			centerRight = _centerRight;
		}
		
	}

}
